﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System;
using System.Reflection;

namespace BrainTechLLC.ThreadSafeObjects
{
    [Serializable]
    [DataContract]
	public class CachedObject
	{
        [DataMember]
		public object Obj { get; set; }
        [DataMember]
		public bool Dirty { get; set; }
	}

	public static class StaticCache
	{
		public static ThreadSafeLookup<string, CachedObject> _cachedObjects = new ThreadSafeLookup<string, CachedObject>();

		public static void MarkDirty(string key)
		{
			CachedObject c = _cachedObjects[key];
			if (c != null)
			{
				c.Dirty = true;
			}
		}

		public static T2 GetValue<T, T2>(this T obj, string propertyName) where T : class
		{
			PropertyInfo pi = typeof(T).GetProperty(propertyName);
			T2 result = (T2)pi.GetValue(obj, null);

			if (default(T2).Equals(result))
			{
				MethodInfo[] mis = typeof(T).GetMethods();
				foreach (MethodInfo mi in mis)
				{
					object[] objs = mi.GetCustomAttributes(typeof(PopulatesProperty), true);
					if (objs != null && objs.Length > 0)
					{
						PopulatesProperty pp = (PopulatesProperty)objs[0];
						if (pp.PopulatesPropertyName.Equals(propertyName, StringComparison.OrdinalIgnoreCase))
						{
							object ret = mi.Invoke(obj, null);
							pi.SetValue(obj, (T2)ret, null);
							return (T2)ret;
						}
					}
				}
			}

			return result;
		}

		public static T GetValue<T>(string key, Func<T> populateCacheDelegate)
		{
			CachedObject c = _cachedObjects[key];
			if (c == null)
			{
				c = new CachedObject();
				c.Obj = populateCacheDelegate();
				_cachedObjects.AddOrSet(key, c);
				return (T)c.Obj;
			}
			else if (c.Dirty)
			{
				c.Obj = populateCacheDelegate();
				c.Dirty = false;
				return (T)c.Obj;
			}
			else
			{
				return (T)c.Obj;
			}
		}
	}
}
